<?php 

class LsCacheBehavior
extends CBehavior
{
	
	public $rewrite_operation;
	
	static public $_cache_events = array(
		'Blog'	=> array(
			'insert' => array(
				'clean'	=> 'blog_new, blog_new_user_{user_owner_id}',
				'delete' => '',
			),
			'update' => array(
				'clean'	=> 'blog_update, blog_update_{blog_id}, topic_update',
				'delete' => 'blog_{blog_id}',
			),
			'delete' => array(
				'clean'	=> 'blog_update, blog_relation_change_blog_{blog_id}, topic_update, comment_online_update_topic, comment_update',
				'delete' => 'blog_{blog_id}, topic_{topics.topic_id}',
			),
		),
		'BlogUser'	=> array(
			'insert' => array(
				'clean'	=> 'blog_relation_change_{user_id}, blog_relation_change_blog_{blog_id}',
				'delete' => 'blog_relation_user_{blog_id}_{user_id}'
			),
			'update' => array(
				'clean'	=> 'blog_relation_change_{user_id}, blog_relation_change_blog_{blog_id}',
				'delete' => 'blog_relation_user_{blog_id}_{user_id}',
			),
			'delete' => array(
				'clean'	=> 'blog_relation_change_{user_id}, blog_relation_change_blog_{blog_id}',
				'delete' => 'blog_relation_user_{blog_id}_{user_id}',
			),
		),
		'City'	=> array(
			'insert' => array(
				'clean'	=> 'city_new',
				'delete' => '',
			),
			'update' => array(
				'clean'	=> 'city_new',
				'delete' => '',
			),
			'delete' => array(
				'clean'	=> 'city_new',
				'delete' => '',
			),
		),
		'CityUser'	=> array(
			'insert' => array(
				'clean'	=> 'city_new',
				'delete' => '',
			),
			'update' => array(
				'clean'	=> 'city_new',
				'delete' => '',
			),
			'delete' => array(
				'clean'	=> 'city_new',
				'delete' => '',
			),
		),
		'Comment'	=> array(
			'insert' => array(
				'clean'	=> 'comment_new_{target_type}, comment_new_user_{user_id}_{target_type}, comment_new_{target_type}_{target_id}',
				'delete' => '',
			),
			'update' => array(
				'clean'	=> 'comment_new_{target_type}, comment_update, comment_update_{target_type}_{target_id}, comment_update_rating_{target_type}, comment_update_status_{target_type}',
				'delete' => 'comment_{comment_id}',
			),
			'delete' => array(
				'clean'	=> 'comment_new_{target_type}, comment_new_user_{user_id}_{target_type}, comment_new_{target_type}_{target_id}, comment_update, comment_update_{target_type}_{target_id}, comment_update_rating_{target_type}, comment_update_status_{target_type}',
				'delete' => 'comment_{comment_id}',
			),
		),
		'CommentOnline'	=> array(
			'insert' => array(
				'clean'	=> 'comment_online_update_{target_type}',
				'delete' => '',
			),
			'update' => array(
				'clean'	=> 'comment_online_update_{target_type}',
				'delete' => '',
			),
			'delete' => array(
				'clean'	=> 'comment_online_update_{target_type}',
				'delete' => '',
			),
		),
		'Country'	=> array(
			'insert' => array(
				'clean'	=> 'country_new',
				'delete' => '',
			),
			'update' => array(
				'clean'	=> 'country_new',
				'delete' => '',
			),
			'delete' => array(
				'clean'	=> 'country_new',
				'delete' => '',
			),
		),
		'CountryUser'	=> array(
			'insert' => array(
				'clean'	=> 'country_new',
				'delete' => '',
			),
			'update' => array(
				'clean'	=> 'country_new',
				'delete' => '',
			),
			'delete' => array(
				'clean'	=> 'country_new',
				'delete' => '',
			),
		),
		'Favourite'	=> array(
			'insert' => array(
				'clean'	=> 'favourite_{target_type}_change_user_{user_id}',
				'delete' => 'favourite_{target_type}_{target_id}_{user_id}'
			),
			'update' => array(
				'clean'	=> 'favourite_{target_type}_change, favourite_{target_type}_change_user_{user_id}',
				'delete' => 'favourite_{target_type}_{target_id}_{user_id}',
			),
			'delete' => array(
				'clean'	=> 'favourite_{target_type}_change_user_{user_id}, favourite_{target_type}_change',
				'delete' => 'favourite_{target_type}_{target_id}_{user_id}',
			),
		),
		'Friend'	=> array(
			'insert' => array(
				'clean'	=> 'friend_change_user_{user_from}, friend_change_user_{user_to}',
				'delete' => 'user_friend_{user_from}_{user_to}, user_friend_{user_to}_{user_from}'
			),
			'update' => array(
				'clean'	=> 'friend_change_user_{user_from}, friend_change_user_{user_to}',
				'delete' => 'user_friend_{user_from}_{user_to}, user_friend_{user_to}_{user_from}'
			),
			'delete' => array(
				'clean'	=> 'friend_change_user_{user_from}, friend_change_user_{user_to}',
				'delete' => 'user_friend_{user_from}_{user_to}, user_friend_{user_to}_{user_from}'
			),
		),
		'Invite'	=> array(
			'insert' => array(
				'clean'	=> 'invate_new_to_{user_to_id}, invate_new_from_{user_from_id}',
				'delete' => '',
			),
			'update' => array(
				'clean'	=> 'invate_new_to_{user_to_id}, invate_new_from_{user_from_id}',
				'delete' => '',
			),
			'delete' => array(
				'clean'	=> 'invate_new_to_{user_to_id}, invate_new_from_{user_from_id}',
				'delete' => '',
			),
		),
		'Page'	=> array(
			'insert' => array(
				'clean'	=> 'page_change, page_change_{page_id}, page_change_urlfull_{page_url_full}',
				'delete' => '',
			),
			'update' => array(
				'clean'	=> 'page_change, page_change_{page_id}, page_change_urlfull_{page_url_full}',
				'delete' => '',
			),
			'delete' => array(
				'clean'	=> 'page_change, page_change_{page_id}',
				'delete' => '',
			),
		),
		'Session'	=> array(
			'insert' => array(
				'clean'	=> 'user_session_update',
				'delete' => 'user_session_{user_id}'
			),
			'update' => array(
				'clean'	=> 'user_session_update',
				'delete' => 'user_session_{user_id}'
			),
			'delete' => array(
				'clean'	=> 'user_session_update',
				'delete' => 'user_session_{user_id}'
			),
		),
		'Talk'	=> array(
			'insert' => array(
				'clean'	=> 'talk_new, talk_new_user_{user_id}',
				'delete' => '',
			),
			'update' => array(
				'delete' => 'talk_{talk_id}',
				'delete' => '',
			),
			'delete' => array(
				'clean'	=> 'talk_new, talk_new_user_{user_id}',
				'delete' => 'talk_{talk_id}'
			),
		),
		'TalkUser'	=> array(
			'insert' => array(
				'clean'	=> 'update_talk_user_{talk_id}, update_talk_user',
				'delete' => '',
			),
			'update' => array(
				'clean' => 'update_talk_user_{talk_id}, update_talk_user, talk_read_user_{user_id}',
				'delete' => 'talk_{talk_id}, talk_user_{talk_id}_{user_id}'
			),
			'delete' => array(
				'clean' => 'update_talk_user_{talk_id}, update_talk_user',
				'delete' => 'talk_{talk_id}, talk_user_{talk_id}_{user_id}'
			),
		),
		'Topic'	=> array(
			'insert' => array(
				'clean'	=> 'topic_new, topic_update_user_{user_id}, topic_new_blog_{blog_id}',
				'delete' => '',
			),
			'update' => array(
				'clean' => 'topic_update_user_{user_id}, topic_update, topic_update_blog_{blog_id}, topic_new_blog_{blog_id}',
				'delete' => 'topic_{topic_id}',
			),
			'delete' => array(
				'clean' => 'topic_update_user_{user_id}, topic_update, topic_update_blog_{blog_id}',
				'delete' => 'topic_{topic_id}',
			),
		),
		'TopicContent'	=> array(
			'insert' => array(
				'clean'	=> 'topic_new, topic_update_user_{topic.user_id}, topic_new_blog_{topic.blog_id}',
				'delete' => '',
			),
			'update' => array(
				'clean' => 'topic_update_user_{topic.user_id}, topic_update, topic_update_blog_{topic.blog_id}, topic_new_blog_{topic.blog_id}',
				'delete' => 'topic_{topic.topic_id}'
			),
			'delete' => array(
				'clean' => 'topic_update_user_{topic.user_id}, topic_update, topic_update_blog_{topic.blog_id}',
				'delete' => 'topic_{topic.topic_id}'
			),
		),
		'TopicQuestionVote'	=> array(
			'insert' => array(
				'clean'	=> 'topic_question_vote_user_{user_voter_id}',
				'delete' => 'topic_question_vote_{topic_id}_{user_voter_id}',
			),
			'update' => array(
				'clean' => 'topic_question_vote_user_{user_voter_id}',
				'delete' => 'topic_question_vote_{topic_id}_{user_voter_id}'
			),
			'delete' => array(
				'clean' => 'topic_question_vote_user_{user_voter_id}',
				'delete' => 'topic_question_vote_{topic_id}_{user_voter_id}'
			),
		),
		'TopicRead'	=> array(
			'insert' => array(
				'clean'	=> 'topic_read_user_{user_id}',
				'delete' => 'topic_read_{topic_id}_{user_id}',
			),
			'update' => array(
				'clean'	=> 'topic_read_user_{user_id}',
				'delete' => 'topic_read_{topic_id}_{user_id}',
			),
			'delete' => array(
				'clean'	=> 'topic_read_user_{user_id}',
				'delete' => 'topic_read_{topic_id}_{user_id}',
			),
		),
		'TopicTag'	=> array(
			'insert' => array(
				'clean'	=> 'topic_update, topic_new',
				'delete' => 'topic_{topic_id}',
			),
			'update' => array(
				'clean'	=> 'topic_update, topic_new',
				'delete' => 'topic_{topic_id}',
			),
			'delete' => array(
				'clean'	=> 'topic_update, topic_new',
				'delete' => 'topic_{topic_id}',
			),
		),
		'User'	=> array(
			'insert' => array(
				'clean'	=> 'user_new',
				'delete' => '',
			),
			'update' => array(
				'clean'	=> 'user_new',
				'delete' => 'user_{user_id}'
			),
			'delete' => array(
				'clean'	=> 'user_new',
				'delete' => 'user_{user_id}'
			),
		),
		'UserAdministrator'	=> array(
			'insert' => array(
				'clean'	=> 'user_new',
				'delete' => 'user_{user_id}'
			),
			'update' => array(
				'clean'	=> 'user_new',
				'delete' => 'user_{user_id}'
			),
			'delete' => array(
				'clean'	=> 'user_new',
				'delete' => 'user_{user_id}'
			),
		),
	);
	
	protected $_cache_tags = array();
	protected $_cache;
	
	
	public function events()
	{
		return array(
			'onBeforeSave'		=> 'LsCacheARBeforeSave',
			'onAfterSave'		=> 'LsCacheARAfterSave',
			'onBeforeDelete'	=> 'LsCacheARBeforeDelete',
			'onAfterDelete'		=> 'LsCacheARAfterDelete',
		);
	}
	
	protected function replaceRecursive($tag_template, $tr){
		$tags = array();
		foreach($tr as $match => $variations){ break; }
		foreach($variations as $variation){
			$tags[] = str_replace($match, $variation, $tag_template);
		}
		unset($tr[$match]);
		if(!$tr){
			return $tags;
		}
		$new_tags = array();
		foreach($tags as $tag){
			$new_tags = array_merge($new_tags, $this->replaceRecursive($tag, $tr));
		}
		return $new_tags;
	}
	
	protected function tagTemplateToTags($object, $tag_template){
		if(is_array($object)){
			$tags = array();
			foreach($object as $o){
				$tags = array_merge($tags, $this->tagTemplateToTags($o, $tag_template));
			}
			return $tags;
		}
		if(!preg_match_all('#\{([^\}]+)\}#', $tag_template, $rr, PREG_SET_ORDER)){
			return array($tag_template);
		}
		$next_level = array();
		foreach($rr as $r){
			$match = $r[0];
			$keys = explode('.', $r[1]);
			$key = array_shift($keys);
			$item = $object->$key;
			if(!count($keys)){
				$tag_template = str_replace($match, $item, $tag_template);
			}elseif(empty($next_level[$key])){
				$next_level[$key][] = array($match, $item, $keys);
			}
		}
		if(!$next_level){
			return array($tag_template);
		}
		$tr = array();
		foreach($next_level as $key => $aa){
			foreach($aa as $a){
				list($match, $item, $keys) = $a;
				$sub_template = '{'.join('.', $keys).'}';
				$sub_variations = $this->tagTemplateToTags($item, $sub_template);
				$tr[$match] = $sub_variations;
			}
		}
		$tags = $this->replaceRecursive($tag_template, $tr);
		return $tags;
	}
	
	public function buildCacheTags(LsActiveRecord $model, $delete=false, $add_tags=false){
		$class = get_class($model);
		if(!($operation = $this->rewrite_operation)){
			$operation = $delete ? 'delete' : ($model->getIsNewRecord() ? 'insert' : 'update');
		}
		if(!$add_tags){
			$this->_cache_tags = array('clean' => array(), 'delete' => array());
		}
		if(empty(self::$_cache_events[$class][$operation])){
			return;
		}
		$tags = &$this->_cache_tags;
		
		$tag_lists = self::$_cache_events[$class][$operation];
		foreach($tag_lists as $list_name => $list){
			$list = preg_split('#,\s*#ms', $list, -1, PREG_SPLIT_NO_EMPTY);
			foreach($list as $tag_template){
				$new_tags = $this->tagTemplateToTags($model, $tag_template);
				if($new_tags){
					$tags[$list_name] = array_merge($tags[$list_name], $new_tags);
					$tags[$list_name] = array_unique($tags[$list_name]);
				}
			}
		}
	}
	
	public function runCacheClean(){
		$tags = $this->_cache_tags;
		$this->_cache_tags = array();
		if(!$this->_cache){
			$t = Yii::E()->GetModule('Cache_x');
			$this->_cache = $t[0];
		}
		if(!empty($tags['clean'])){
			Yii::trace(__METHOD__.': cache clean ('.join(', ', $tags['clean']).')');
			$this->_cache->Clean(Zend_Cache::CLEANING_MODE_MATCHING_TAG, $tags['clean']);
		}
		if(!empty($tags['delete'])){
			Yii::trace(__METHOD__.': cache delete ('.join(', ', $tags['delete']).')');
			foreach($tags['delete'] as $tag){
				$this->_cache->Delete($tag);
			}
		}
	}
	
	public function LsCacheARBeforeSave(CModelEvent $e){
		$this->rewrite_operation = null;
		if($this->getOwner()->getIsNewRecord()){
			$this->_cache_tags = array('clean' => array(), 'delete' => array());
			$this->rewrite_operation = 'insert';
			return;
		}
		$this->buildCacheTags($this->getOwner());
	}
	
	public function LsCacheARAfterSave(CEvent $e){
		$model = $this->getOwner();
		if($this->rewrite_operation = 'insert'){
			$model->refresh();
		}
		$this->buildCacheTags($model, false, true);
		$this->runCacheClean();
	}
	
	public function LsCacheARBeforeDelete(CModelEvent $e){
		$this->rewrite_operation = null;
		$this->buildCacheTags($this->getOwner(), true);
	}
	
	public function LsCacheARAfterDelete(CEvent $e){
		$this->runCacheClean();
	}
	
}

?>